package de.codi.similarity.filler;

import org.semanticweb.owlapi.model.OWLAnnotation;
import org.semanticweb.owlapi.model.OWLClass;
import org.semanticweb.owlapi.model.OWLClassExpression;
import org.semanticweb.owlapi.model.OWLDataProperty;
import org.semanticweb.owlapi.model.OWLDataPropertyExpression;
import org.semanticweb.owlapi.model.OWLIndividual;
import org.semanticweb.owlapi.model.OWLLiteral;
import org.semanticweb.owlapi.model.OWLNamedIndividual;
import org.semanticweb.owlapi.model.OWLObjectProperty;
import org.semanticweb.owlapi.model.OWLObjectPropertyExpression;
import org.semanticweb.owlapi.model.OWLOntology;

import de.codi.read.Storage;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;


public class IndividualFiller
{
    private HashMap<OWLNamedIndividual, HashMap<OWLNamedIndividual, Double>> result;


    public IndividualFiller(Set<OWLNamedIndividual> source, Set<OWLNamedIndividual> target)
    {
        fill(source, target);
    }


    private void fill(Set<OWLNamedIndividual> source, Set<OWLNamedIndividual> target)
    {
        this.result = new HashMap<OWLNamedIndividual, HashMap<OWLNamedIndividual, Double>>();

        for(OWLNamedIndividual s : source) {
            IndividualInformation si = new IndividualInformation(s, Storage.source.ont);
            HashMap<OWLNamedIndividual, Double> tmp = new HashMap<OWLNamedIndividual, Double>();
            for(OWLNamedIndividual t : target) {
                IndividualInformation ti = new IndividualInformation(t, Storage.target.ont);
                               
                if(si.compare(ti) == 1.0) {
                    tmp.put(t, 1.0);
                }
            }
            if(tmp.size() > 0) {
                result.put(s, tmp);
            }
        }
    }


    public double compare(Set<OWLNamedIndividual> source, Set<OWLNamedIndividual> target)
    {
        if(source.size() == 0 || target.size() == 0) { return 0; }

        Set<OWLNamedIndividual> tSet = new HashSet<OWLNamedIndividual>();

        for(OWLNamedIndividual s : source) {
            if(result.containsKey(s)) {
                tSet.addAll(result.get(s).keySet());
            }
        }

        if(tSet.size() == 0) { return 0; }

        for(OWLNamedIndividual t : target) {
            if(!tSet.contains(t)) { return 0; }
        }

        return ((double) Math.min(source.size(), target.size())) / Math.max(source.size(), target.size()) / 1;
    }

    class IndividualInformation
    {
        private OWLOntology                         var;
        private OWLNamedIndividual                  ni;

        public String                               label, name;
        public Map<String, HashMap<String, String>> classExpression;
        public Map<String, String>                  dataProperties;
        public Set<String>                          objectProperties;


        public IndividualInformation(OWLNamedIndividual ni, OWLOntology o)
        {
            this.var = o;
            this.ni = ni;
            read();
        }


        private void read()
        {
            name = ((OWLNamedIndividual) ni).getIRI().getFragment();
            Set<OWLAnnotation> as = ((OWLNamedIndividual) ni).getAnnotations(var);
            for(OWLAnnotation a : as) {
                if(a.getValue() instanceof OWLLiteral) {
                    OWLLiteral val = (OWLLiteral) a.getValue();
                    label = val.getLiteral();
                }
            }

            // ClassExpressions
            classExpression = new HashMap<String, HashMap<String, String>>();
            Set<OWLClassExpression> ce = ni.getTypes(var);
            for(OWLClassExpression c : ce) {
                if(c instanceof OWLClass) {
                    OWLClass cl = (OWLClass) c;
                    HashMap<String, String> tmp = new HashMap<String, String>();
                    for(OWLAnnotation a : cl.getAnnotations(var)) {
                        if(a.getValue() instanceof OWLLiteral) {
                            OWLLiteral val = (OWLLiteral) a.getValue();
                            tmp.put(a.getProperty().getIRI().getFragment(), val.getLiteral());
                        }
                    }
                    classExpression.put(cl.getIRI().toString().replace(cl.getIRI().getStart(), ""), tmp);
                }
            }

            // ObjectProperties
            objectProperties = new HashSet<String>();
            Map<OWLObjectPropertyExpression, Set<OWLIndividual>> pe = ni.getObjectPropertyValues(var);
            for(OWLObjectPropertyExpression ope : pe.keySet()) {
                if(ope instanceof OWLObjectProperty) {
                    OWLObjectProperty op = (OWLObjectProperty) ope;
                    objectProperties.add(op.getIRI().toString().replace(op.getIRI().getStart(), ""));
                }
            }

            // DataProperties
            dataProperties = new HashMap<String, String>();
            Map<OWLDataPropertyExpression, Set<OWLLiteral>> de = ni.getDataPropertyValues(var);
            for(OWLDataPropertyExpression dpe : de.keySet()) {
                String dprop = "";
                if(dpe instanceof OWLDataProperty) {
                    OWLDataProperty dp = (OWLDataProperty) dpe;
                    dprop = dp.getIRI().toString().replace(dp.getIRI().getStart(), "");
                }
                for(OWLLiteral lit : de.get(dpe)) {
                    dataProperties.put(dprop, lit.getLiteral());
                }
            }
        }


        public double compare(IndividualInformation ii)
        {
            if (name.equalsIgnoreCase(ii.name)){
                return 1;
            }
            
            for(String s : dataProperties.keySet()) {
                if(ii.dataProperties.containsKey(s) && !dataProperties.get(s).equals(ii.dataProperties.get(s))) { return 0; }
            }

            for(String s : objectProperties) {
                if(!ii.objectProperties.contains(s)) { return 0; }
            }

            if(label != null && ii.label!=null && !label.equals(ii.label)) { return 0; }

            if(dataProperties.keySet().size() == 0 && objectProperties.size() == 0 && label == null) { return 0; }

            return 1;
        }
    }
}
